using System;
using System.Collections.Generic;
using full_leaf_framework.Visual;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace touhou_bullet_rain;

public class Projectile {

    public Drawable selfDrawable;

    public string bulletType;
    public string uuid;
    public Vector2 pos;
    private Vector2 posCorrectSwift = Vector2.Zero;
    public Vector2 velocity = Vector2.Zero;
    public float angle = 0;
    public float hitPoints = 10.0f;

    private Level level;

    /// <summary>
    /// 弹幕类型对应的相关信息
    /// </summary>
    public static Dictionary<string, ProjectileData> bulletDatas = new Dictionary<string, ProjectileData>
    {
        { "normal_bullet",     new ProjectileData { WeaponName = "Normal Shot",
            RefreshTime = 1, VelocityChangePerSecond = 0.0f } },
        { "reimu_spellcard",   new ProjectileData { WeaponName = "Reimu Spell Rifle",
            RefreshTime = 2, VelocityChangePerSecond = -15.0f } },
        { "marisa_star",       new ProjectileData { WeaponName = "Marisa Star Machine Gun",
            RefreshTime = 2, VelocityChangePerSecond = 75.0f } },
        { "reimilia_arrow",    new ProjectileData { WeaponName = "Reimilia Scarlet Arrow",
            RefreshTime = 1, VelocityChangePerSecond = 70.0f } },
        { "cirno_ice",         new ProjectileData { WeaponName = "Cirno Ice Rail Gun",
            RefreshTime = 2, VelocityChangePerSecond = 0.0f } },
        { "alice_doll",        new ProjectileData { WeaponName = "Alice Doll Pair",
            RefreshTime = 1, VelocityChangePerSecond = 0.0f } },
        { "keine_mirror",      new ProjectileData { WeaponName = "Keine Permanent Launcher",
            RefreshTime = 1, VelocityChangePerSecond = -100.0f } },
        { "keine_mirror_sub",  new ProjectileData { WeaponName = "Report this to developer!",
            RefreshTime = 1, VelocityChangePerSecond = 100.0f } },
        { "mokou_flame",       new ProjectileData { WeaponName = "Mokou Flame Thrower",
            RefreshTime = 2, VelocityChangePerSecond = -340.0f } },
        { "sanae_star",        new ProjectileData { WeaponName = "Sanae Star Shot Gun",
            RefreshTime = 2, VelocityChangePerSecond = -50.0f } },
        { "koishi_love",       new ProjectileData { WeaponName = "Koishi Heartbeat Sniper",
            RefreshTime = 4, VelocityChangePerSecond = 0.0f } },
        { "murasa_anchor",     new ProjectileData { WeaponName = "Murasa Anchor Launcher",
            RefreshTime = 1, VelocityChangePerSecond = 70.0f } },
        { "murasa_anchor_sub", new ProjectileData { WeaponName = "Report this to developer!",
            RefreshTime = 1, VelocityChangePerSecond = 120.0f } },
        { "seija_arrow",       new ProjectileData { WeaponName = "Seija Backward Rapid Arrow",
            RefreshTime = 2, VelocityChangePerSecond = 130.0f } },
        { "doremy_catcher",    new ProjectileData { WeaponName = "Doremy Dream Catcher",
            RefreshTime = 2, VelocityChangePerSecond = 20.0f } },
        { "okina_star",        new ProjectileData { WeaponName = "Okina WideSpread Star Rail Gun",
            RefreshTime = 2, VelocityChangePerSecond = 30.0f } },
        { "yachie_shield",     new ProjectileData { WeaponName = "Yachie Sheild Rifle",
            RefreshTime = 1, VelocityChangePerSecond = -150.0f } },
        { "yachie_shield_sub", new ProjectileData { WeaponName = "Report this to developer!",
            RefreshTime = 2, VelocityChangePerSecond = -130.0f } },
        { "zanmu_ghost",       new ProjectileData { WeaponName = "Zanmu WideSpread Ghost Launcher",
            RefreshTime = 2, VelocityChangePerSecond = 125.0f } },
    };

    public string ownerUuid;

    public Projectile(string uuid, string bulletType, Vector2 pos,
    Vector2 velocity, string ownerUuid, Level level) {
        this.uuid = uuid;
        this.bulletType  = bulletType;
        this.pos = pos;
        posCorrectSwift = Vector2.Zero;
        this.level = level;
        this.velocity = velocity;
        this.ownerUuid = ownerUuid;
        try {
            var sprite = new AnimatedSprite(GameManager.Content.Load<Texture2D>("Projectile/" + bulletType));
            selfDrawable = new Drawable(sprite, pos, Vector2.Zero);
        }
        catch { selfDrawable = null; }
    }

    public void HandleServerRequest(string[] paramsList) {
        try {
            posCorrectSwift = new Vector2(float.Parse(paramsList[1]),
                float.Parse(paramsList[2])) - pos;
            velocity = new Vector2(float.Parse(paramsList[3]),
                float.Parse(paramsList[4]));
        }
        catch (Exception e) {
            Console.WriteLine("Error When Handle Request -> " + e);
        }
    }

    private Player FindNearestPlayer() {
        Player playerTarget = null; float playerDist = float.MaxValue;
        foreach (Player currPlayer in level.Players.Values) {
            if (Vector2.Distance(currPlayer.pos, pos) < playerDist &&
            ownerUuid != currPlayer.uuid) {
                playerDist = Vector2.Distance(currPlayer.pos, pos);
                playerTarget = currPlayer;
            }
        }
        return playerTarget;
    }

    private Youkai FindNearestYoukai() {
        Youkai youkaiTarget = null; float youkaiDist = float.MaxValue;
        foreach (Youkai currYoukai in level.Youkais.Values) {
            if (Vector2.Distance(currYoukai.pos, pos) < youkaiDist &&
            ownerUuid != currYoukai.uuid) {
                youkaiDist = Vector2.Distance(currYoukai.pos, pos);
                youkaiTarget = currYoukai;
            }
        }
        return youkaiTarget;
    }

    private Vector2 Turn(Vector2 target, float turnAngle) {
        float length = target.Length();
        float currAngle = Player.VectorToAngle(target);
        currAngle += turnAngle;
        return new Vector2(MathF.Cos(currAngle) * length, MathF.Sin(currAngle) * length);
    }

    private void Guidance(GameTime gameTime, float turnVelocity) {
        // 模拟服务端的制导
        float currAngle = Player.VectorToAngle(velocity);
        Player playerTarget = FindNearestPlayer();
        float playerDist = playerTarget is null ? float.MaxValue : Vector2.Distance(playerTarget.pos, pos);
        Youkai youkaiTarget = FindNearestYoukai();
        float youkaiDist = youkaiTarget is null ? float.MaxValue : Vector2.Distance(youkaiTarget.pos, pos);
        // 找到最近的单位
        if (playerTarget is not null || youkaiTarget is not null) {
            float rate = (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (playerDist < youkaiDist) {
                // 转向最近的玩家
                float distAngle = Player.VectorToAngle(playerTarget.pos - pos);
                if (((distAngle - currAngle < 0) ? (distAngle - currAngle + MathF.PI * 2.0f) : (distAngle - currAngle)) <
                ((currAngle - distAngle < 0) ? (currAngle - distAngle + MathF.PI * 2.0f) : (currAngle - distAngle))) {
                    velocity = Turn(velocity, turnVelocity * rate);
                    // 顺时针旋转
                }
                else {
                    velocity = Turn(velocity, -turnVelocity * rate);
                    // 逆时针旋转
                }
            }
            else {
                // 转向最近的妖怪
                float distAngle = Player.VectorToAngle(youkaiTarget.pos - pos);
                if (((distAngle - currAngle < 0) ? (distAngle - currAngle + MathF.PI * 2.0f) : (distAngle - currAngle)) <
                ((currAngle - distAngle < 0) ? (currAngle - distAngle + MathF.PI * 2.0f) : (currAngle - distAngle))) {
                    velocity = Turn(velocity, turnVelocity * rate); // 顺时针旋转
                }
                else {
                    velocity = Turn(velocity, -turnVelocity * rate); // 逆时针旋转
                }
            }
        }
    }

    public void Update(GameTime gameTime) {
        selfDrawable.pos = pos;
        if (bulletDatas.ContainsKey(bulletType)) {
            for (int i = 1; i <= bulletDatas[bulletType].RefreshTime; i++) {
                pos += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            velocity *= (velocity.Length() + bulletDatas[bulletType].VelocityChangePerSecond *
            (float)gameTime.ElapsedGameTime.TotalSeconds) / velocity.Length();
        }
        // 处理本地的一些更新，例如计算角度，速度
        if (bulletType == "marisa_star" || bulletType == "sanae_star" ||
        bulletType == "okina_star") {
            selfDrawable.angle += (float)gameTime.ElapsedGameTime.TotalSeconds * MathF.PI * 2;
        }
        else {
            selfDrawable.angle = Player.VectorToAngle(velocity);
        }
        // 让制导弹幕偏转角度
        if (bulletType == "reimu_spellcard") {
            Guidance(gameTime, MathF.PI / 4.0f);
        }
        if (bulletType == "reimilia_arrow") {
            Guidance(gameTime, MathF.PI / 8.0f);
        }
        if (bulletType == "doremy_catcher") {
            Guidance(gameTime, MathF.PI / 6.0f);
        }
        if (bulletType == "zanmu_ghost") {
            Guidance(gameTime, MathF.PI / 12.0f);
        }
        // 修正弹道偏移
        CorrectCurrPos(gameTime);
        // 让当前坐标插值接近目标坐标（强制拉近）
    }

    private void CorrectCurrPos(GameTime gameTime) {
        // 平滑插值使得对象移动同步偏差量
        float distance = posCorrectSwift.Length();
        float approachVelocity = 30f + distance * 1.5f + distance * distance * 0.5f;
        approachVelocity *= (float)gameTime.ElapsedGameTime.TotalSeconds;
        if (distance <= approachVelocity) {
            pos += posCorrectSwift;
            posCorrectSwift = Vector2.Zero;
        }
        else {
            Vector2 currSwift = posCorrectSwift;
            currSwift.Normalize(); currSwift *= approachVelocity;
            pos += currSwift;
            posCorrectSwift -= currSwift;
        }
    }

    public void Draw(Camera camera) {
        if (selfDrawable is not null) { camera.insertObject(selfDrawable); }
    }

}